package threads;

import messages.CompetenceValidationRequestMessage;
import messages.CompetenceValidationResponseMessage;
import messages.DocumentSigningRequestMessage;
import messages.DocumentSigningResponseMessage;
import messages.NonceToSignRequestMessage;
import messages.NonceToSignResponseMessage;
import messages.SignatureValidationRequestMessage;
import messages.SignatureValidationResponseMessage;
import security.SecureRandomNumber;
import server.ServerData;
import connection.TCPSecureServerSocket;
import connection.exception.ConnectionResetException;

class CertcopClientThread extends Thread{

	private TCPSecureServerSocket _socket;
	private ServerData _serverData;

	CertcopClientThread(TCPSecureServerSocket socket){
		_socket = socket;
		_serverData = ServerData.getServerData();
	}

	public void run(){
		try {
			_socket.waitChannelEstablishmentMode();

			while(_serverData.isOnline() || _socket.isSecureChannelEstablished()){
				Object msg = _socket.receive();

				if(msg == null)
					continue;

				// Mensagem de Pedido de nonce para Autenticação de Utilizador
                if(msg.getClass().getName().equals("messages.NonceToSignRequestMessage")) {

                    // Fazer Cast à mensagem
                    NonceToSignRequestMessage req = (NonceToSignRequestMessage) msg;

                    // Obter nonce para devolver
                    Integer nonce = SecureRandomNumber.generateNonce();
                    
                    // Registar nonce Localmente
                    String fName = req._firstName;
                    String lName = req._lastName;
                    String comp = req._competence;
                    _serverData.addNonce(nonce, fName, lName, comp);
                    
                    // Enviar Resposta
                    NonceToSignResponseMessage res = new NonceToSignResponseMessage(nonce);
                    _socket.send(res);
                }
                // Mensagem de Verificação de competência
                else if(msg.getClass().getName().equals("messages.CompetenceValidationRequestMessage")) {

				    // Fazer Cast à mensagem
				    CompetenceValidationRequestMessage req = (CompetenceValidationRequestMessage) msg;
				    
				    // TODO Verificação de Autenticação
				    String sig =  req._signature;
				    Integer nonce = req._nonce;
				    Boolean authResult = _serverData.isCompetenceSignatureValid(sig, nonce);
				    
				    // Verificação de Competência
                    String[] name = _serverData.getUserByNonce(nonce);
                    String firstName = name[0];
                    String lastName = name[1];
				    String competency = req._competence;
				    Boolean result = _serverData.userHasCompetency(firstName, lastName, competency);
				    
				    // Remover nonce (já não é necessário) e enviar Resposta
				    _serverData.removeNonce(nonce);
				    CompetenceValidationResponseMessage res = new CompetenceValidationResponseMessage(result && authResult);
					_socket.send(res);

				} // Mensagem de pedido de Assinatura de Documentos 
                else if(msg.getClass().getName().equals("messages.DocumentSigningRequestMessage")) {

                    // Fazer Cast à mensagem
                    DocumentSigningRequestMessage req = (DocumentSigningRequestMessage) msg;
                    
                    // TODO Verificação de Autenticação
                    String sig =  req._nonceSignature;
                    Integer nonce = req._nonce;
                    Boolean authResult = _serverData.isCompetenceSignatureValid(sig, nonce);
                    
                    // Obter dados para assinatura, caso necessários
                    String[] name = ServerData.getServerData().getUserByNonce(nonce);
                    String firstName = name[0];
                    String lastName = name[1];
                    String competence = req._competence;
                    
                    // Remover nonce (já não é necessário)
                    _serverData.removeNonce(nonce);
                    
                    // Se a verificação falhar, enviar mensagem de erro vazia, e termina
                    if(!authResult) {
                        DocumentSigningResponseMessage res = 
                            new DocumentSigningResponseMessage(false, "", "");
                        _socket.send(res);
                        continue;
                    }

                    // Obter Documento a Assinar
                    String file = req._file;
                    
                    // Obter se é uma assinatura "VERICOP"
                    Boolean isVericop = req._hasPriority;
                    
                    // Obter duração
                    Integer duration = req._duration;
                    
                    // Obter Certificado e Assinatura, do Documento
                    String[] sigAndCert = _serverData.generateSignature(file, firstName + lastName, competence, isVericop, duration);
                    String certStruct = sigAndCert[0];
                    String certSignature = sigAndCert[1];
                    
                    // Enviar Resposta
                    DocumentSigningResponseMessage res = 
                        new DocumentSigningResponseMessage(true, certStruct, certSignature);
                    _socket.send(res);

                } // Mensagem de Valicação de Assinatura por VERICOP
                else if(msg.getClass().getName().equals("messages.SignatureValidationRequestMessage")) {

                    // Fazer Cast à mensagem
                    SignatureValidationRequestMessage req = (SignatureValidationRequestMessage) msg;                    
                    
                    // TODO Verificação da Assinatura VERICOP 
                    Integer serial = req._signatureSerial;
                    String sigReceived = req._signatureSignedValue;
                    Boolean result = _serverData.isVericopSignatureValid(sigReceived, serial); 
                    
                    // Enviar Resposta
                    SignatureValidationResponseMessage res = new SignatureValidationResponseMessage(serial, result);
                    _socket.send(res);

                } else {
					throw new RuntimeException("Received an Unsupported Message Type: " + msg.getClass().getName());
				}
			}

		} catch (ConnectionResetException e1) {
			e1.printStackTrace();
		}
	}
}
